package org.wevil.util;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.context.ConfigurableApplicationContext;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Bean工具类
 */
public class BeanUtil {

    public static ConfigurableApplicationContext applicationContext;

    // ----------------------------公有方法------------------------------------//

    /**
     * 获取bean
     */
    public static <T> T getBean(Class<T> c) {
        return applicationContext.getBean(c);
    }

    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    /**
     * 拷贝对象，从src拷贝到target。
     * src里空值不会拷贝到target
     * src里有的值则会覆盖target里的值
     */
    public static void copyPropIgnoreNull(Object src, Object target) {
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }

    /**
     * 将 map 转换为 bean 对象
     *
     * @param map   需要转换的 map 集合
     * @param clazz 需要转换的 bean 类型
     * @return 转换的对象
     * @author wanglei
     * @since 2023/10/25
     */
    public static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) {
        T obj;
        ArrayList<String> nameList = new ArrayList<>(); // 初始化需要设置属性的集合
        try {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                String methodName = method.getName(); // 获取方法名
                // 判断该方法是否为 set 方法
                if (methodName.startsWith("set")) {
                    // 获取当前 set 的参数类型和名称
                    String name = methodName.substring(3);
                    name = Introspector.decapitalize(name);
                    nameList.add(name); // 将需要set的值放入集合
                }
            }
            // 实例化对象
            obj = clazz.newInstance();
            // 获取该类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true); // 设置为 true 才能给属性设置值
                if (nameList.contains(field.getName())) {
                    field.set(obj, map.get(field.getName()));  // 设置属性
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return obj;
    }


    // ----------------------------私有方法------------------------------------//

    /**
     * 获取为空的对象字段
     *
     * @param source 对象
     */
    private static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (java.beans.PropertyDescriptor pd : pds) {
            String name = pd.getName();
            try{
                Object srcValue = src.getPropertyValue(name);
                if (srcValue == null) {
                    emptyNames.add(name);
                }
            }catch (org.springframework.beans.NotReadablePropertyException nrpe){
                System.out.println("不可读的属性：" + pd.getClass().getName() + "." + name);
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

}
